Skip to content

Adr 0004#3

Open
lzrscg wants to merge 109 commits intomainfrom
adr-0004
Open

Adr 0004#3
lzrscg wants to merge 109 commits intomainfrom
adr-0004

Conversation

@lzrscg
Copy link
Copy Markdown
Contributor

@lzrscg lzrscg commented Apr 20, 2026

No description provided.

lzrscg and others added 30 commits April 20, 2026 10:49
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, lazy env-snapshot errors

Address review feedback by:
- Rewriting the Decision Summary cleanup bullet to match the detailed
  rules (symlink replacements are unlinked; only identity-matched
  directories are recursively removed).
- Adding "child launch / spawn failure after tmpdir creation" as an
  explicit terminal outcome so the tmpdir guarantee covers cases like a
  discovered script being removed between discovery and spawn or an OS
  rejection of a RunOptions.env entry.
- Clarifying that the shell env-var prefix on the CLI reuses the
  inherited-env tier and does not introduce a new override-precedence
  level above -e or global env.
- Preserving SPEC §9.1's sync-never-throws guarantee when
  RunOptions.env is an exotic object (Proxy, throwing getter) by
  routing snapshot exceptions through the pre-iteration error path.
- Spelling out the five-tier env precedence list in the §8 SPEC-update
  mapping and extending §7.2 to cover spawn-path failures.

Status remains Proposed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expands ADR-0004 with two new decisions that make scripts easier to
write:

- §3 Project-root cwd. Scripts run with LOOPX_PROJECT_ROOT as their
  child-process cwd instead of the workflow directory, superseding
  SPEC §6.1's current "workflow directory as cwd" rule. Eliminates
  the \$ROOT="\$LOOPX_PROJECT_ROOT" prefix boilerplate currently
  repeated in every workflow script.
- §4 LOOPX_WORKFLOW_DIR injection. Absolute path to the
  currently-spawned script's workflow directory, refreshing per-spawn
  alongside LOOPX_WORKFLOW. Correct across intra-workflow goto,
  cross-workflow goto (destination sees its own dir, not caller's),
  deep chains, and loop reset. Gives scripts a one-token reference
  to workflow-local assets without dirname/import.meta.url tricks.

Also corrects SPEC §3.3's stale claim that closer-node_modules/loopx
precedence is "a natural consequence of running scripts with the
workflow directory as cwd" — module resolution is file-relative, not
cwd-relative.

Renamed from 0004-tmpdir-and-env.md → 0004-script-execution-context.md
to reflect the broader scope (now four decisions: LOOPX_TMPDIR,
RunOptions.env, project-root cwd, LOOPX_WORKFLOW_DIR).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rget

Apply review feedback: weaken the async-generator cleanup claim (no
GC-based guarantee), make the absolute discovery-time invocation path
a normative rule so $0 / import.meta.url equal LOOPX_WORKFLOW_DIR,
list §5.1 in Affected SPEC Sections so its "non-zero exit" reference
aligns with §7.2's new spawn-failure category, note that
LOOPX_PROJECT_ROOT is symlink-preserving like LOOPX_WORKFLOW_DIR,
drop src/env.ts and src/execution.ts implementation refs, and correct
the "loopx env set" consequence to reflect its persistent-global role.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…y path spelling and warning cardinality

Address review feedback pre-acceptance:

- JS/TS dirname(fileURLToPath(import.meta.url)) equality with LOOPX_WORKFLOW_DIR
  is now non-normative under symlinks (Node canonicalizes the main module's URL
  by default; Bun's behavior is unspecified). Bash $(dirname \"\$0\") equality
  remains normative. LOOPX_WORKFLOW_DIR is authoritative for JS/TS code.
- Replace \"path-spelling preserved\" prose with \"no extra canonicalization by
  loopx\" for LOOPX_PROJECT_ROOT, explicitly acknowledging that process.cwd()
  may canonicalize while RunOptions.cwd is used verbatim.
- Normalize cleanup warning cardinality to one stderr warning per distinct
  anomaly, no aggregation or dedup; test-friendly.
- Retarget \"§3.2 / §7.1 pre-iteration\" to §7.1 only; add §6.2 and §6.3
  entries for the language-specific invocation-path consequences.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ats to entry scripts

Closes the logical gap between "LOOPX_PROJECT_ROOT is absolute" and the
prior "RunOptions.cwd used verbatim" wording: a relative cwd is now
resolved via path.resolve(process.cwd(), options.cwd) at call time with
no further realpath. Broadens the JS/TS import.meta.url equality and
§3.3 module-resolution caveats to cover symlinked entry script files
(not only symlinked workflow directories), matching SPEC §5.1's symlink
policy on both. Adds a one-line note that OS-rejected RunOptions.env
entries don't surface under maxIterations: 0 since no spawn occurs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mlink scope, tighten warning cardinality

- Context: fix confused `.loopx/shared/` sentence (workflow-local paths
  break after cross-workflow goto because $LOOPX_WORKFLOW updates).
- §2 RunOptions.env: add "inherited process.env snapshotted once per
  run" rule so deterministic env is a per-run property, not just a
  RunOptions.env property.
- §1 warning cardinality: replace "any other distinct cleanup error
  surface" catch-all with a closed enumeration of three categories
  (leave-in-place, top-level recursive-removal failure, partial-dir
  cleanup failure on creation error).
- §3 / §4 / Affected-SPEC / Tests: consistently frame JS/TS
  import.meta.url non-guarantee around any symlink in the absolute
  discovery-time entry path (workflow dir, entry script file, or
  intermediate component) rather than only the first two. Frame the
  loopx guarantee around what loopx controls (absolute invocation
  path + LOOPX_WORKFLOW_DIR).
- Consequences: fix incorrect claim that `-e`/`loopx env set` reach
  "above env files" (`-e` is local env file tier; `loopx env set`
  writes the global env file tier). Replace RunOptions.env rationale:
  it exists as an in-memory per-call override layer that (a) avoids
  mutating process.env, (b) avoids writing a temp env file, and (c)
  layers on top of RunOptions.envFile — not because programmatic
  callers lack an -e equivalent (they have RunOptions.envFile).
- Tests: add inherited-env snapshot test, intermediate-symlink
  scenario test, and updated warning-cardinality assertions to match
  the closed enumeration.

Status remains Proposed.
…eanup

Addresses review feedback on ADR-0004:

- Distinguish effective-cwd (loopx-controlled) from cwd string spelling
  (runtime-reported); runtime APIs may canonicalize while LOOPX_PROJECT_ROOT
  preserves caller spelling.
- Document that PWD is not a protocol variable; loopx neither sets nor
  unsets it, Bash regenerates $PWD on startup.
- Specify partial tmpdir cleanup when identity capture fails: single
  non-recursive rmdir attempt, then leave-in-place + category-3 warning.
- Make RunOptions.env shape validation explicit under maxIterations: 0.
- Extend Affected SPEC Sections list with §5.1 symlink-path preservation
  and §12 tmpdir/spawn exit-code coverage; note envFile resolution under
  the new cwd semantics.
- Soften "module resolution is unaffected by cwd" to apply specifically
  to script module resolution, not cwd-dependent tooling.
- Scope byte-for-byte cwd test assertions to symlink-free fixtures; add
  filesystem-identity assertions for symlinked regimes.
- Add Rejected / Deferred Alternatives section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix symbol-key contradiction in RunOptions.env shape validation (symbol-keyed
entries are ignored, not invalid). Replace path.resolve with fs.realpath in
the symlink-identity test convention (path.resolve is string-only and does
not prove filesystem identity). Simplify warning cardinality to reflect that
cleanup dispatches on a single top-level LOOPX_TMPDIR path, so each of the
three categories fires at most once per cleanup and the three are mutually
exclusive. Document the deliberate lazy-vs-call-time asymmetry between
inherited process.env (lazy) and RunOptions.env / RunOptions.cwd (call-time),
with a corresponding test recommendation. Clarify that RunOptions.envFile
shares the local-env-file precedence tier with -e, and that invalid
RunOptions.env shape / snapshot exceptions are pre-spawn failures that do
not create a tmpdir.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes four internal inconsistencies flagged during review of ADR-0004:

- Warning cardinality: categorize by cleanup routine (full safety vs.
  non-recursive rmdir), not by terminal-vs-creation outcome. Identity-
  captured creation-failure cleanup now correctly emits cat 1/2 rather
  than claiming mutually-exclusive-with-cat-3 while sharing a routine.
- Closed-set language: switch to strict version — implementations emit
  no cleanup warnings outside the three categories; tests may assert
  their absence.
- CLI symlink spelling: drop claim that CLI invocation through a symlink
  can preserve shell-side spelling. LOOPX_PROJECT_ROOT (CLI) is exactly
  process.cwd() at invocation; loopx does not consult \$PWD. Symlinked-
  project-root asymmetry reachable only via programmatic RunOptions.cwd.
- Custom resolve hook: explicitly require the Node/tsx hook to preserve
  standard module resolution precedence via defaultResolve delegation,
  so workflow-local node_modules/loopx still wins after the cwd change.

Also clarifies RunOptions.env inspection timing (synchronous at call
time, errors deferred to pre-iteration) and notes that cleanup safety
does not detect mount points inside \$LOOPX_TMPDIR.

Status remains Proposed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s from review

Fix contradictions and close gaps flagged in ADR-0001 review feedback:

- Correct the relative-cwd symlink claim: symlinked components in a
  relative RunOptions.cwd are preserved by path.resolve(), so a
  symlink-preserving LOOPX_PROJECT_ROOT is not restricted to symlinked
  absolute cwd inputs.
- Broaden cleanup warning category 2 from "Top-level recursive-removal
  failure" to "Top-level cleanup failure" covering lstat dispatch
  failure, top-level symlink unlink failure, and identity-matched
  recursive removal failure; preserves the at-most-one-warning model.
- Make abort precedence across pre-iteration explicit: an already-
  aborted signal wins over validation, discovery, env loading, target
  resolution, version check, and tmpdir creation errors.
- Document .loopx itself as a symlink: treated as an intermediate
  component of the absolute entry path with its spelling preserved.
- Capture options.env getter exceptions through the lazy pre-iteration
  error path, like snapshot exceptions.
- Tighten module-resolution hook language: any standard file-relative
  resolution wins over the CLI fallback, not only workflow-local.
- Add SPEC §9.3 to Affected Sections; extend §7.2 bullet to cover
  tmpdir creation failure alongside other runtime error paths.
- Add test recommendations for cleanup-internal failures, relative cwd
  with symlinked components, and symlinked .loopx directory entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… timing

Blocking fix: revise RunOptions.env malformed-name behavior. Node's
child_process does not reliably reject names containing "=" (a key like
"A=B" with value "C" may reach the child as A=B=C rather than a spawn
failure). Reframe runtime-level rejection around embedded NUL bytes as
the reliable case, note that other malformed-but-runtime-accepted names
have runtime-dependent child-observed behavior, and update the matching
test recommendation.

Also clarify: run() yields any final Output before the generator
settles, with cleanup guaranteed before { done: true } / abort
surfacing; abort precedence applies only once options.signal is
captured (a throwing signal getter is an ordinary snapshot error);
summary lists consumer-driven generator cancellation as a terminal
outcome; SPEC §3.3 update language uses "any standard file-relative
resolution of loopx wins over the CLI fallback" instead of the
workflow-local-implying "closer node_modules" phrasing; "OS rejection"
normalized to "runtime rejection" for consistency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dering, and cleanup caveats

Incorporate review feedback into ADR-0004 ahead of acceptance:

- Warning cardinality: each cleanup warning must contain exactly one of
  three bracketed category tokens ([leave-in-place-refusal],
  [top-level-cleanup-failure], [partial-directory-cleanup-failure]) so
  tests can assert category identity without matching implementation-
  defined prose.
- Options snapshot ordering: options.signal is read before options.env /
  cwd / envFile / maxIterations, making the existing "abort wins over
  options-snapshot exceptions" rule reachable when combined with a
  throwing env getter. Each option field is read at most once per call.
- Non-abort pre-iteration error priority is implementation-defined
  except where already specified (e.g., invalid env shape + missing
  .loopx/ may surface either error).
- Identity-capture-failure regime: document that the single rmdir is
  best-effort and may remove an unrelated empty directory if a same-user
  process swaps the mkdtemp directory before cleanup; this remains
  outside the race-resistant guarantee, as a deliberate trade-off
  against leaking the partial directory on the common identity-capture-
  failure path.
- Promote the run() final-yield cleanup caveat into the Decision Summary
  LOOPX_TMPDIR bullet: cleanup on normal completion is guaranteed only
  once the async generator is driven to settlement; the final yielded
  Output is not itself settlement.
- Soften device/inode wording in normative text to behavioral identity
  language, retaining device/inode as one POSIX implementation strategy.

Update Affected SPEC Sections and Test Recommendations to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, pre-iteration error scoping, and CLI grammar

- Extend AbortSignal precedence to cover target argument validation
  (non-string `target`) and target syntax validation (empty, bare
  colon, etc.) — both previously left ambiguous.
- Add CLI signal precedence paragraph: SIGINT/SIGTERM during the CLI
  pre-iteration phase mirrors programmatic `AbortSignal` precedence.
- Scope the implementation-defined non-abort pre-iteration priority
  rule to the programmatic API only; CLI retains SPEC §7.1's concrete
  ordering.
- Narrow "each option field read at most once" to permit normal
  one-pass enumeration of `options.env` proxies (single `ownKeys` plus
  per-property traps as required by the JS enumeration algorithm);
  additional enumeration passes and getter retries remain forbidden.
- Add explicit pre-first-`next()` consumer-cancellation carve-out:
  `.return()` / `.throw()` on a generator that has not yet had
  `next()` invoked settles per standard async-generator semantics
  without observing signal state, even when the captured signal is
  already aborted. `runPromise()` has no equivalent carve-out.
- Clarify call-time option snapshot vs lazy pre-iteration sequence so
  the "`RunOptions.env` shape validation" placement is unambiguous.
- Pin "no CLI named-argument syntax" as an explicit §4.1/§4.2 affected
  SPEC bullet so the decision is traceable from this ADR.
- Make the cleanup-warning category-token taxonomy and
  at-most-one-warning-per-attempt cardinality an explicit stable
  public testing contract.
- Update §7.3, §9.1/§9.2, and §9.3 affected-SPEC bullets to reflect
  the new carve-outs; add test recommendations covering them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ield, run -h, warning scoping, and invalid options

Addresses acceptance-level blockers raised in review:
- Pre-first-next() consumer cancellation now explicitly wins over all
  captured call-time option snapshot errors (throwing option-field
  getters, invalid env shapes, invalid options values, invalid
  options.signal values), not only already-aborted signals.
- Abort after the final yielded Output is explicitly spelled out as
  Option A — abort still wins until generator settlement; the final
  yield does not commit normal completion.
- The "no CLI named-argument syntax" rule now explicitly preserves the
  existing SPEC §4.2 `run -h` / `--help` short-circuit, both in the
  decision summary and in the §4.1 / §4.2 affected-SPEC entry.
- Cleanup-warning token requirement is scoped narrowly to LOOPX_TMPDIR
  cleanup warnings; other loopx warnings (version-check, unreadable
  package.json, invalid-entry, invalid env-file line, etc.) are
  outside the taxonomy and must not carry these tokens.
- Invalid `options` (null, number, string, array, etc.) and invalid
  `options.signal` (non-AbortSignal-compatible values) are now
  defined: both are captured at call time and surfaced via the
  standard pre-iteration error path; a non-compatible signal does
  not enter the abort-precedence pathway.

Also adds a timing-model terminology note at the start of §2
(call-time capture / pre-iteration surfacing / spawn-time runtime
rejection) and corresponding test recommendations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ith SPEC, broaden env-tier rejection, clarify LOOPX_DELEGATED and hard-link semantics

Review feedback blockers and clarifications:

- Bash `$PWD` / bare `pwd` are no longer claimed to equal
  `LOOPX_PROJECT_ROOT` byte-for-byte in any regime. Per POSIX, Bash
  retains an inherited `PWD` that names the current directory through
  symlinks, so a symlinked-logical ancestor shell can leave the child
  with a `$PWD` that differs from even a canonical
  `LOOPX_PROJECT_ROOT`. Tests must use `pwd -P` / `/bin/pwd` or
  filesystem identity, not `$PWD` / bare `pwd`.
- "version-check error" removed from pre-iteration error-priority
  lists in §1 abort precedence, CLI signal precedence, non-abort
  priority, and the §7.3 / §9.3 affected-SPEC sections and matching
  test recommendation. SPEC §3.2 defines version-check outcomes as
  non-fatal warnings, so version checking is not a competing
  terminal-outcome mode; the ADR now says so explicitly.
- Spawn-time runtime rejection language broadened from "a
  `RunOptions.env` entry" to "any child environment entry from any
  env tier (inherited, env file, `RunOptions.env`)" in §1 Cleanup,
  §1 Timing-model phase 3, affected §7.2, §9.3, §12, and the
  matching test recommendation.
- `LOOPX_DELEGATED` explicitly documented as remaining an internal
  delegation guard outside this ADR's script-protocol-variable
  tier; `RunOptions.env` and env files may supply it to children
  without a loopx-side per-spawn override.
- Hard-link refusal clarified: case 3 applies to top-level path
  replacement only; recursive cleanup under case 4 may unlink
  hard-link entries inside the identity-matched tmpdir, as
  ordinary recursive deletion would.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ping, recursive-removal test, version-check-under-n0 example, and make cwd/envFile validation explicit

- Abort precedence: open paragraph now leads with the "usable signal captured" qualifier; removes the contradictory listing of invalid options / invalid options.signal as items abort can displace (these cases capture no signal and are surfaced as ordinary options-snapshot errors). Ripple fix to the §9.3 Affected SPEC bullet.
- Cleanup-token scoping: tests may now assert that known non-cleanup warnings lack the three bracketed tokens (previously forbidden by the "or lack" clause, which contradicted the normative scoping rule).
- Recursive-removal failure test: split the three category-2 sub-cases so only the lstat-failure and symlink-unlink-failure cases claim "path left in place"; the recursive-removal-failure sub-case no longer promises an intact tmpdir, since partial deletion may have occurred.
- Token-scoping "no-token run" example: replaced version-mismatch-under-maxIterations:0 (never emits, since SPEC §3.2 / §7.1 / §4.2 skip version checking under -n 0) with env-file invalid-line per SPEC §8.1.
- Explicit validation for options.cwd / options.envFile / options.maxIterations values: new subsection defines the string-only requirement for cwd/envFile, captures non-string values at call time, and surfaces via the standard pre-iteration error path. Propagated to the pre-first-next() carve-out list, the non-abort priority enumeration, §9.1/§9.2, §9.3, §9.5 Affected SPEC bullets, and a new test recommendation.

Status remains Proposed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ummary, and LOOPX_DELEGATED role distinction

Addresses acceptance-blocker review feedback: scope the public-contract
surface explicitly, separate startup-reserved from script-protocol-protected
for LOOPX_DELEGATED, tighten runPromise inherited-env timing as
implementation-defined, and make name=value target-validation plus CLI
signal precedence for target syntax explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… -- grammar, §3.2, and cleanup-token escaping

Incorporate reviewer's acceptance-blocker feedback without marking the ADR
Accepted:

- runPromise() inherited-env snapshot timing: make implementation-defined
  relative to return (may capture at sync setup OR async continuation),
  resolving the prior contradiction between "asynchronously after return"
  and the "either outcome is conforming" test license.
- CLI signal-wins precedence: scope explicitly to after the run -h / --help
  short-circuit and usage-level argument parsing. Parser-level usage errors
  (no target, multiple positionals, duplicate -n/-e, unrecognized flags,
  etc.) remain parser-level and are not displaced by signal-wins.
- `--` grammar: reject `--` in any position in `run` outside the help
  short-circuit. No POSIX end-of-options support either; added as a
  Rejected / Deferred Alternative.
- §3.2 — Project Root: add to Affected SPEC Sections to make project-root
  derivation rules (CLI = own process.cwd() without $PWD/realpath;
  programmatic = resolved RunOptions.cwd or process.cwd() at call time)
  explicit.
- Global env-file path resolution: align timing with env-file loading
  (first next() for run(), implementation-defined for runPromise()) so
  XDG_CONFIG_HOME / HOME lookup timing is unambiguous.
- CLI inherited-env test: remove the misleading "concurrent process
  modifies loopx env" claim; make the per-run snapshot mutation test
  programmatic-only, since POSIX forbids external env mutation.
- Cleanup-warning category tokens: require the token in a loopx-controlled
  prefix position AND require implementations to escape / quote
  user-controlled text so TMPDIR-influenced paths cannot introduce a
  second apparent token. Cardinality-of-one remains observable.

Status stays Proposed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…snapshot timing, tighten impl-defined scope and CLI grammar terminology

Address review-pass feedback. Narrow the "implementation-defined by default" sentence so it cannot be read as de-specifying existing SPEC guarantees outside this ADR's change surface. Rewrite the cleanup-warning "Token placement and user-controlled text" rule: the normative requirement is that user-controlled text cannot contribute an additional plain-text occurrence of any token; prefix placement alone is insufficient when paths are also rendered, and merely surrounding user-controlled text in quotes is not sufficient because a quoted string still contains the token substring. Rename the inherited-env subsection to "per-API split, not uniformly 'lazy'" so the text no longer simultaneously claims run()/runPromise() share lazy semantics while also allowing synchronous call-time capture under runPromise(); update cross-references in §2, §7.2, §9.1/§9.2 accordingly. Add an explicit RunOptions.env enumeration algorithm (Object.entries-equivalent semantics, with observable per-key getOwnPropertyDescriptor probes and captured snapshot errors on throwing probe traps), resolving the ambiguity between "non-enumerable own string keys are ignored" and "one-pass enumeration may invoke per-property traps." Split the CLI §4.1 / §4.2 test recommendation so target-validation failures (loopx run name=value) are not lumped under "usage errors"; tests may no longer require a usage-style diagnostic for that form. Add the throwing-addEventListener subcase to the invalid-options.signal rule and clarify that removeEventListener is not part of the compatibility contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…coping, tmpdir parent, option-first priority, duck-typed signal reentrancy

- Fix internal contradiction in §1 "Scope of CLI signal-wins precedence" / §7.3 Affected: pre-handler-installation window no longer claims both "usage error surfaces regardless of signal" and "signals follow POSIX default disposition"; both outcomes are now explicitly conforming and tests must tolerate either.
- Reword CLI signal-wins rule around handler observation rather than OS-level delivery, so implementations that run synchronous pre-iteration work are not required to poll / async-ify to meet the "signal wins" contract.
- Loosen non-cleanup-warning token rule from "must not contain any of the three tokens" to "must not use them as loopx-controlled category markers", permitting incidental occurrences in user-controlled text (env-file lines, paths) and removing a sanitization burden on every non-cleanup warning site.
- Add §1 "Parent directory selection" clarifying that os.tmpdir() is evaluated in the loopx process environment; env-file entries and RunOptions.env do not redirect LOOPX_TMPDIR parent selection.
- Pin project-root-blocking / envFile-path-blocking option errors (non-string / throwing options.cwd / options.envFile) ahead of non-options pre-iteration failures in the programmatic-API priority rule; retain implementation-defined ordering for the remaining non-abort bucket.
- Add §1 "Duck-typed signal reentrancy" rules: synchronous abort-listener invocation or observed-aborted during capture treats signal as aborted; already-aborted real AbortSignal must be observed as aborted; ordering between reading .aborted and registering listener is otherwise implementation-defined for duck-typed signals only.
- Update Scope section to list new implementation-defined caveats (pre-handler-installation signal ordering, delivered-but-unobserved signal vs. synchronous pre-iteration failure, duck-typed signal latitude) and new pinned contract items (project-root-blocking option-error ordering, reserved-as-marker cleanup token scoping).
- Update Test Recommendations to match all of the above.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tightening pass on ADR-0004 surfacing work:

- scope summary rule 5 and detailed non-abort priority section so the
  pinned options.cwd / options.envFile ordering only beats the four
  project-root-dependent failures; relative priority against target
  argument / syntax validation is now explicitly implementation-defined
  (resolves prior contradiction between summary and detailed text)
- carve the inherited-env snapshot timing and call-time option
  snapshotting out of the "pre-iteration runs asynchronously after
  return" rule for runPromise(), matching §2's per-API split
- add cleanup idempotence rule (at most one cleanup attempt per created
  LOOPX_TMPDIR) so per-run warning cardinality is bounded to one across
  racing terminal triggers
- reject function values in RunOptions.env shape, matching the options
  parameter's existing function rejection
- simplify TMPDIR / runPromise() guidance to "before calling
  runPromise()" since user code cannot mutate between call and return
- drop Deno references — SPEC §1 targets only Node.js and Bun
- clarify loopx run name=value grammar classification ("target, not
  named argument" is stable) vs. observable outcome under §7.1, which
  may surface a discovery error first in a project without .loopx/

Status remains Proposed pending separate acceptance.
…tion

Per review feedback: the ADR was doing the work of an ADR plus a future
SPEC patch plus a TEST-SPEC at once. Tightened to focus on the spec
changes themselves so the ADR can fully drive the SPEC.md update and
then retire from the working set.

Specific changes (all per maintainer guidance on the four clarification
points raised in review):

- v1-scope `--` rejection on `loopx run`: framed as parser simplification
  arising from "no named-argument tail," not a permanent product stance.
  Future ADR may revisit alongside a named-argument surface.
- Cleanup warning category tokens demoted to implementation-defined.
  Kept the normative behavior (at most one cleanup attempt per tmpdir,
  at most one warning per attempt) but dropped the public token contract,
  cardinality-by-token-match rules, and user-controlled-text escaping
  rules. Tests assert via provenance, not plain-text token matching.
- `runPromise()` inherited-`process.env` snapshot pinned to call-time
  (synchronous, before return). Removes the previous "implementation-
  defined, bounded by no-later-than-first-spawn" latitude. `run()`
  retains lazy capture at first `next()`. Symmetric with `RunOptions.env`
  and `RunOptions.cwd` capture timing.
- `RunOptions.env` one-pass enumeration / per-trap invocation pattern
  demoted to implementation-defined. Kept "each field read at most once
  per call, no retry after throws" as the SPEC-level guarantee.

Other tightening:
- Removed the ~900-word "Scope of this ADR" preamble that locked
  observable edge-case mechanics as stable contract.
- Removed the standalone "Decision Summary" section (its content was
  duplicated by the §1-§4 decision sections themselves).
- Trimmed Test Recommendations from ~95 bullets to ~9 high-risk edge
  cases. Per ADR-0001 the section is for cases easy to overlook, not
  an exhaustive test plan.
- Promoted CLI grammar from a Decision-Summary bullet to a top-level
  §5 Decision section so the v1-scoping is obvious.
…ace precedence, tmpdir hardening, §7.4

- Broaden pre-first-next() .return()/.throw() carve-out to suppress
  all pre-iteration errors (invalid target, target syntax, discovery,
  env-file loading, target resolution, tmpdir creation) in addition to
  option-snapshot errors, and fix the "without observing signal state"
  wording that conflicted with call-time signal capture.
- Add catch-all terminal-outcome precedence: first trigger observed by
  loopx wins among genuinely racing terminal triggers that no explicit
  precedence rule covers; idempotence and warning cardinality are
  independent of outcome selection.
- Separate RunOptions.env shape validation (programmatic, surfaces
  under maxIterations: 0) from CLI -n 0 behavior (no RunOptions.env
  surface) so the "-n 0 validates env shape" claim is no longer
  misleading.
- Pin tmpdir creation order as mkdtemp → identity capture → mode
  securing, and define per-step creation-failure cleanup
  (no-op / single rmdir / full cleanup-safety routine) in that order.
- Snapshot the tmpdir parent (os.tmpdir() / TMPDIR / TEMP / TMP) on
  the same schedule as the inherited process.env snapshot: synchronous
  for runPromise(), at first next() for run(). Eliminates the
  runPromise() race where a post-return process.env.TMPDIR mutation
  could have silently affected the tmpdir parent.
- Introduce a new §7.4 "Run-scoped temporary directory lifecycle" in
  the Affected SPEC Sections list, covering parent selection, location/
  naming/mode, creation order, identity-fingerprint cleanup safety,
  renamed-away and mount-point behavior, race-resistance scope, absence
  of stale-tmpdir reaping, and settlement-based cleanup on run().
- Qualify the incorrect "no existing workflow script uses this pattern"
  claim: .loopx/shared/dispatch.sh uses cwd-relative ./send-*.sh and
  ./node_modules/.bin/tsx references that require migration.
- Redefine LOOPX_WORKFLOW_DIR as the directory portion of the cached
  absolute discovery-time script path, making the Bash $(dirname "$0")
  equality robust to trailing slashes, symlinks, and lexical
  normalization without recomposing from $LOOPX_PROJECT_ROOT /
  .loopx / $LOOPX_WORKFLOW.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…loopx install

New decision bundled into ADR-0004: loopx install spawns npm install
post-commit in each committed workflow with a top-level package.json.
Adds --no-install opt-out to install-scoped CLI (§4.2). Retires the
existing §10.9 manual-install rule and adds a new §10.10. Rationale
surfaced during analysis of how to optimize existing workflow flows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lzrscg and others added 30 commits April 26, 2026 14:56
Fix T-API-55c/55d/65p/65q to construct option objects directly instead
of via object-spread, so throwing getters/proxies are preserved as
accessors on the object passed to run()/runPromise() rather than being
invoked at the test call site. Add a general "test-construction sanity
for getter/proxy variants" note in §1.3 explaining the rule.

Add T-INST-112h (top-level .gitignore lstat failure other than ENOENT
via a new gitignore-lstat-fail seam), T-INST-112i (FIFO .gitignore via
gitignore-replace-with-fifo seam), and T-INST-112j (socket .gitignore
via gitignore-replace-with-socket seam) to close the previously-tracked
SPEC 10.10 safeguard-failure coverage gaps. Update §1.4 seam table and
Appendix A row 10.10 accordingly.

Create SPEC-PROBLEMS.md with P-0004-02: signal handling during the
post-commit auto-install pass when no npm install child is currently
active (SPEC 10.10's "Signals during npm install" clause covers only
the active-child case). TEST-SPEC.md does not add conformance tests
for the no-active-child windows until the clause is clarified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolve P-0004-02 with a SPEC 10.10 clarification covering signal
handling during the auto-install pass when no npm child is active,
delete SPEC-PROBLEMS.md, fold the resolution note into TEST-SPEC §9,
and apply four targeted TEST-SPEC.md refinements: T-API-62f2 and the
descriptor-trap variants of T-API-68i / T-API-69h are reframed as
observational (descriptor-trap invocation is implementation-defined,
not a SPEC requirement) and dropped from direct traceability claims;
T-API-51f closes the run() full env-precedence-chain surface gap;
T-INST-112k records the regular-but-unreadable .gitignore
permission-non-inspection sub-path as a known gap with a recommended
seam; and T-TMP-12d / T-TMP-12e known-gap notes now subsume the
signal/abort × creation-failure race forms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Convert T-INST-116h/i/j/k to ordinal pause windows so they no longer
assume an unspecified commit order; define the parent-observable JSON
marker contract for the LOOPX_TEST_AUTOINSTALL_PAUSE seam; clarify the
gitignore-make-unreadable seam's mode-restoration responsibility (loopx
leaves mode 000, harness restores in afterEach); add T-TMP-13b for run()
generator natural-settlement cleanup, T-TMP-18b for SIGINT escalation +
tmpdir cleanup, and T-API-10f-throw for active-child .throw()
process-group termination; update Appendix A traceability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests / known-gap notes for ADR-0004 coverage edges identified in
review:

- T-API-66c/d/e + T-TMP-24e/f/g: abort-after-final-yield variants for
  the `stop: true`-driven final-yield trigger (existing T-API-66/66a/66b
  + T-TMP-24a/24c/24d only covered the `maxIterations`-reached trigger).
- T-API-10c4 + T-API-10c5: live signal that aborts after `run()` /
  `runPromise()` returns × `maxIterations: 0` (complementing the
  pre-aborted-signal coverage in T-API-10c2 / T-API-10c3).
- T-API-64m + T-API-64m2: duck-signal `addEventListener` getter throws
  on read (distinct from the function-throws-on-call case in
  T-API-64f / T-API-64g).
- T-INST-116l: known-gap note for the "aggregate report already
  emitted" carve-out half of SPEC 10.10's signal-termination clause,
  with implementer note for a `post-aggregate-report` pause-seam value.
- T-TMP-35f/g/h: cleanup-warning-does-not-affect-outcome coverage on
  the signal-terminal (CLI) and abort-terminal (`runPromise()`,
  `run()`) surfaces.
- Tmpdir Parent Snapshot Timing: literal-path expectation preface
  reinforcing `fs.mkdtemp`-allocated parents over fixed `/tmp/...`
  placeholders for T-TMP-25 onward.

No SPEC.md changes — review found no ADR-0004 ambiguities requiring
spec edits, and SPEC-PROBLEMS.md remains absent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track T-INST-116l (already-emitted aggregate-report carve-out) as a known
ADR-0004 gap consistently across §1.3, §9 P-0004-02 resolution note, and the
Appendix A SPEC 10.10 row. Fix the T-TMP-12-cli subcase count from "four" to
"five". Add run() generator counterparts for env-file NUL spawn failures
(T-ENV-26f, T-ENV-26g, T-ENV-27d, T-ENV-27e) so the local/global env-file tier
is covered on all three run surfaces. Add T-CLI-RUN-DASHDASH-10/11 to close the
"-- as option operand" parser gap (loopx run -e -- ralph and loopx run ralph -e
--). Relax T-INST-119 to forbid only actual progress indicators (spinners,
progress bars, percentages); plain status lines are not asserted against absent
a SPEC 10.10 clarification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add new tests and strengthen existing ADR-0004 coverage based on
review feedback against SPEC.md:

- T-API-59b / T-API-59c: run() generator-surface counterparts to
  T-API-59 / T-API-59a (RunOptions.env does not redirect global
  env-file path resolution via XDG_CONFIG_HOME / HOME).
- T-API-60b / T-API-60c: run() generator-surface counterparts to
  T-TMP-29b / T-TMP-29c (RunOptions.env does not redirect tmpdir
  parent via TEMP / TMP).
- T-API-65v / T-API-65w: invalid options wrapper + aborted signal
  intersection on runPromise() and run() — pin down the SPEC 9.3
  carve-out that an invalid options value captures no usable signal
  even when the wrapper carries an aborted AbortSignal.
- T-API-51a: strengthen with in-script stat of LOOPX_TMPDIR matching
  T-TMP-30 / T-TMP-31 rigor (proves the protocol-injected value is
  the real loopx-created tmpdir, not a string substitution).
- T-INST-119 / T-INST-119a / T-INST-119a-stderr / T-INST-119b:
  tighten npm stdout/stderr passthrough assertions from substring
  match to exact-standalone-line match, so a buggy implementation
  that prefixed npm lines (e.g., "[npm] npm-stdout-MARKER\n") is
  caught.

SPEC-PROBLEMS.md remains absent — no ADR-0004-scoped SPEC ambiguity
needs to be tracked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Clarify post-exit-first/post-exit:<workflow> seam definition (§1.4) to
  guarantee the npm child's exit and any non-zero-exit aggregate failure
  entry are recorded before the pause begins, so T-INST-116j2 actually
  exercises the "accumulator already holds an npm-non-zero entry" case.
- Fix global-env-file SPEC 8.2 → 8.1 references in T-TMP-12-global-env-
  unreadable, T-TMP-12-cli-global-env-unreadable, T-API-65r, T-API-68o,
  T-API-69n, and T-SIG-30, plus appendix traceability rows: move T-API-68o,
  T-API-69n, T-SIG-30 from row 8.2 to row 8.1; add 8.1 to T-TMP-12 and
  T-TMP-12-cli parent-trailer Spec lists.
- Add T-TMP-12f4: starting-workflow unreadable package.json warning runs
  before tmpdir creation, closing the fourth SPEC 3.2 package.json
  failure-mode branch (the unreadable-file branch was previously left as
  an implicit corollary of T-VER-07).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- T-API-65u: drop the throwing-cwd-getter variant. SPEC 9.5 leaves the
  ordering between reading aborted and registering the duck listener
  implementation-defined, so an impl that reads aborted (false) and
  then captures the cwd-getter throw without registering the listener
  is conformant. The "abort beats option-snapshot failure" axis is
  already covered for real pre-aborted AbortSignals by T-API-65p / 65q.
  Keep the env-file and target-resolution variants where pre-iteration
  timing makes the abort precedence observable independent of intra-
  snapshot ordering.

- T-CLI-RUN-DASHDASH-10/11: replace the "no MARKER leaked into spawned
  env" assertion (not independently observable when no child spawns)
  with an absence-of-SPEC-8.1-invalid-key-warning assertion. The "--"
  file now contains a malformed line "1BAD=warning-if-loaded" so an
  impl that loaded it before usage validation would emit the SPEC 8.1
  warning to stderr regardless of any subsequent spawn.

- T-API-56g, T-API-56h: add positive coverage for non-plain
  RunOptions.env objects. T-API-56g pins the structural-not-nominal
  contract via a class instance; T-API-56h pins that Map is accepted
  as a shape but contributes zero env vars (Map entries aren't own
  enumerable string-keyed properties).

- T-INST-113c2: parameterize the multi-workflow malformed-package.json
  continuation rule over the remaining SPEC 3.2 / 10.10 causes
  (unreadable, invalid-semver, non-string loopx range value), so the
  SPEC 10.10 "other workflows still proceed" clause is exercised
  independently per cause.

Trace matrix updated for SPEC 3.2, 8.1, 9.5, 10.10. No SPEC.md or
SPEC-PROBLEMS.md changes — feedback explicitly notes no ADR-0004-
scoped SPEC ambiguity requires resolution this cycle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds three test groups closing residual ADR-0004 coverage gaps that
were identified in the post-acceptance TEST-SPEC review:

- T-API-21h/i/j: runPromise() counterparts for call-time relative
  envFile and cwd resolution (mirroring T-API-21f/g on the run()
  surface), including the combined relative-cwd × relative-envFile
  case unique to runPromise()'s eager pre-iteration snapshot.
- T-SYM-07b: Bun counterpart for the symlink-free import.meta.url vs.
  LOOPX_WORKFLOW_DIR byte-for-byte equality from SPEC 6.3, with the
  symlink-divergence assertion intentionally omitted (SPEC 6.3 does
  not require Bun to match Node's realpath behavior).
- T-CLI-RUN-DASHDASH-12/13: -n operand-position counterparts to
  T-CLI-RUN-DASHDASH-10/11, closing the "-- cannot slip through as
  an option operand" axis for the second run option that consumes
  an operand.

Also updates the Appendix A traceability matrix rows 4.1, 4.2, 6.1,
6.3, 9.2, 9.5, and 12 to reference the new test IDs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fix T-MOD-03h / T-MOD-03h-bun extension-remapping reliance: import the
  nested helper as ./lib/helper.ts (its actual extension) instead of
  ./lib/helper.js, since SPEC does not specify tsx/Bun .js→.ts remapping.
- Promote the optional run() concurrent-isolation companion in T-API-50f
  to a required test T-API-50g, pinning ADR-0004's no-racy-process.env-
  mutation motivation on both API surfaces.
- Expand T-API-65p / T-API-65q (abort-precedence × option-snapshot) with
  the full enumerated invalid maxIterations set (Infinity, null, "1"
  added to -1, 1.5, NaN) and reword "all remaining ... failures" to
  "representative remaining ... failures" for the env shape/value
  branches, deferring exhaustive env-shape coverage to T-API-53–55 etc.
- Apply the same expansion + rewording to the carve-out tests
  T-API-68i (.return()) and T-API-69h (.throw()).
- Parameterize T-API-55a / T-API-55b over the full SPEC 9.5 invalid-env
  shape and entry-value matrix under maxIterations: 0, ensuring shape
  validation surfaces under the zero-iteration short-circuit on both
  API surfaces.

No SPEC.md changes; no SPEC-PROBLEMS.md added (no ADR-0004-scoped
ambiguity identified by this review cycle).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fix T-CLI-RUN-DASHDASH-08 and T-TMP-12-cli-usage / T-TMP-12-cli-help-with-unknown wording so help short-circuit tests no longer assert "no discovery"; SPEC 11.2 explicitly permits non-fatal run-help discovery/validation. Frame the assertions around "does not enter the execution pre-iteration sequence" and "no LOOPX_TMPDIR" instead.
- Broaden T-API-57b ("=" in RunOptions.env key) to assert only that loopx does not reject as option-shape error and that the spawn either fails or succeeds — drop the over-pinned "variable is observable" branch since runtime parsing of "BAD=KEY" is OS/runtime-dependent.
- Add T-API-72a covering runPromise() inherited-env snapshot reuse across iterations: mutate process.env.MYVAR between iterations under a release-file sentinel and assert both spawns observed the call-site snapshot.
- Add T-INST-110g pinning down auto-install on a tarball source — previous positive-trigger tests are source-type-agnostic in wording but may default to git fixtures, leaving the post-commit auto-install pass on the tarball path uncovered.
- Extend T-API-10h to observe LOOPX_TMPDIR via an external marker and assert tmpdir cleanup runs before generator settlement under SIGKILL escalation, closing the gap between T-TMP-21 (no escalation) and T-API-10g (.return() escalation).
- Strengthen T-TMP-12a / T-TMP-12d / T-TMP-12e with explicit "no cleanup-related stderr warning" assertions on the success-cleanup branches, pairing inversely with T-TMP-12d2 / T-TMP-12e2's positive cleanup-warning assertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address reviewer feedback on TEST-SPEC.md against SPEC.md (ADR-0004 scope):

- Fix T-API-50g deadlock: split out un-awaited next() promises so the
  harness can wait for fixture markers and touch release files before
  awaiting the first yields, matching T-API-50f's drive sequence.
- Clarify withFakeNpm PID observability: add explicit pidFile?: string
  option to FakeNpmOptions and document that per-invocation log entries
  are finalized at shim exit (so tests needing alive-shim PIDs use the
  pidFile, not the log). Updated T-INST-116, T-INST-116c, T-INST-116e,
  T-INST-116f, T-INST-116g, T-INST-119a, and T-INST-119a-stderr.
- Replace "fsync stderr stream" wording in the post-aggregate-report
  seam (and its T-INST-116l / resolved-this-cycle references) with
  "complete/flush the stderr write as far as the runtime stream API
  supports" — fsync(2) on a pipe is not portable and stderr is commonly
  a pipe in tests; fsync remains the marker-file flush requirement.
- Add an implementation-neutral cleanup-warning detection harness rule
  to section 4.7's LOOPX_TMPDIR preface so "exactly one cleanup-related
  warning" assertions across T-TMP-35*, T-TMP-36*, T-TMP-38*, T-TMP-40,
  T-TMP-41, T-TMP-42, T-TERM-03, T-INST-116l, etc. have a concrete
  predicate (test-only structured marker, single-extra-stderr-line
  counting, or known-marker subtraction) that does not pin warning text.

No SPEC.md changes were proposed by the feedback and none are made;
SPEC-PROBLEMS.md remains absent (no ADR-0004-scoped ambiguities).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add T-TMP-08b CLI same-project concurrent tmpdir isolation test, clarify
T-API-55e wording around the wrapper-shape gate firing before the
maxIterations field is read, and pin T-INST-110g's archive-derived
workflow naming so <name> placeholders are unambiguous.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address three reviewer items scoped to ADR-0004:

1. T-API-55e: drop the over-pin on implementation-defined pre-iteration
   priority (SPEC 9.3). The previous fixture combined a syntax-invalid
   target and a missing-`.loopx/` discovery failure with the invalid
   `options` wrapper, asserting wrapper-shape rejection wins — but SPEC
   leaves that priority implementation-defined. Rewritten to use a valid
   project + valid target so the wrapper-shape rejection is the only
   conforming surface, and reframed the `maxIterations: 0` framing
   (wrapper-shape validation runs before field-level processing, so the
   zero-iteration short-circuit is not applicable to this case).

2. Add T-TMP-12e3 covering `mode-secure-fail` × `lstat-fail` — the
   second cleanup-safety failure branch reachable from creation-failure
   handling under SPEC 7.4. Updated the section preamble and the
   section 1.4 seam description to clarify which cleanup-fault values
   are reachable when composed with `mode-secure-fail`
   (`lstat-fail` and `recursive-remove-fail` reachable;
   `symlink-unlink-fail` unreachable because the partial tmpdir is
   never a symlink — documented as a known gap that would need a
   separate seam).

3. SPEC §9.5 enumerates `Proxy ownKeys` traps, throwing enumerable
   getters, and throwing `options.env` getters as snapshot-time-throw
   paths but does not pin down whether the value-read step uses
   ordinary `[[Get]]` semantics (which invokes a Proxy `get` trap) or
   descriptor-based extraction (which does not). Added SPEC-PROBLEMS.md
   tracking the ambiguity and relaxed T-API-62f3 (throwing `get` trap
   surfacing), T-API-62h9 (no-retry counter on `get` trap), and the
   value-read assertions in T-API-52e / T-API-52e2 variant (b) to
   observational status, matching the precedent set by T-API-62f2 for
   the analogous descriptor-trap ambiguity. T-API-62f / T-API-62h5 /
   T-API-62h6 remain strict pins on the SPEC-pinned `ownKeys` axis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolve the open ADR-0004-scoped SPEC ambiguity around RunOptions.env
Proxy get trap semantics by amending SPEC §9.5 / §9.1 to specify
ordinary [[Get]] semantics for value reads (option 1 from
SPEC-PROBLEMS.md). Promote T-API-62f3 / T-API-62h9 / T-API-52e /
T-API-52e2 from observational to strict conformance pins per the new
SPEC clause, and delete SPEC-PROBLEMS.md (no remaining open ADR-0004
problems).

Make the structured cleanup-warning marker (LOOPX_TEST_CLEANUP_WARNING)
a required test-only seam in section 1.4 so cleanup-warning cardinality
tests can reliably distinguish cleanup warnings from implementation-
defined creation-failure / script-failure errors on mixed-stderr
terminals (T-TMP-12d2 / T-TMP-12e2 / T-TMP-12e3 / T-TMP-35c–35e /
T-TMP-40 / T-TMP-41 / T-TMP-42 / T-TMP-42a / T-TMP-42b /
racing-trigger warning-cardinality).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address four feedback items from the review of ADR-0004 TEST-SPEC.md
coverage:

1. List the cleanup-safety symlink-`unlink` branch within
   creation-failure handling under `mode-secure-fail` as a known gap
   in the section 1.3 inline list and the Appendix A 7.4 row, since
   the partial directory created by `mkdtemp` is never a symlink and
   cleanup-safety rule-2 dispatch is unreachable in that composition
   without an additional swap-to-symlink seam.

2. Remove the stale "excluding the known-gap IDs T-TMP-12d / 12e /
   38 / 39" exclusion clause from the Appendix A 7.4 row (those
   tests are now seam-backed direct conformance pins, not gaps), and
   add T-TMP-12e3 to the section 1.3 "previously seam-dependent gaps
   promoted to direct pins" summary.

3. Add T-TMP-38d2 — the symmetric counterpart to T-TMP-38d on the
   inverted observation order (post-final-yield consumer `.throw()`
   observed first, racing abort during cleanup-start). Closes the
   matrix-symmetry gap on the warning-cardinality axis of SPEC 7.2's
   "abort after final yield concurrent with consumer `.throw()`"
   enumeration. Trace into Appendix A 7.2 / 7.4 / 9.1 / 9.3 rows.

4. Clarify the `withFakeNpm` `logFile` field contract so the shim is
   required to install a Bash `EXIT` trap that finalizes the
   per-invocation log entry under SIGINT / SIGTERM termination,
   matching the assertions in T-INST-116b / 116b2 / 116j / 116j2.
   SIGKILL remains intentionally uncovered (no userspace trap runs);
   the relevant escalation tests (T-INST-116c / 116f) assert on PID
   termination instead of the exit-time log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fix T-TMP-38d2 outcome assertion to silent clean completion (done: true)
  per SPEC 9.1's no-active-child consumer-cancellation rule, aligning with
  T-TMP-22d / T-TMP-22f.
- Move LOOPX_TEST_CLEANUP_WARNING structured marker seam into section 1.4
  Internal Test Seams; leave section 4.7 as a reference back to it.
- Replace Appendix A row 13's stale "T-TMP-12d/12e/38/39 known-gap" exclusion
  with the actual residual LOOPX_TMPDIR known gaps (T-TMP-43, T-TMP-44,
  T-SIG-29, T-SIG-30b / T-API-10j).
- Add T-API-64n / T-API-64n2: prototype-inherited duck-signal acceptance
  pinning SPEC 9.5's "expose" wording against own-property-only readings.
- Add T-INST-116h2 / 116i2 / 116k2 / 116l2: SIGTERM-axis parity for the
  no-active-child auto-install windows; SPEC 10.10 names "SIGINT / SIGTERM"
  together for these windows.
- Add T-TMP-38a2: mid-loop .return()-first vs. abort-during-cleanup
  cleanup-warning cardinality test, completing matrix symmetry on the
  .return() axis (counterpart to T-TMP-38a).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add tests for previously uncovered API surfaces (run() generator
counterparts to runPromise()-only coverage), extend tier coverage of
local-env-file non-redirection of global env-file lookup, and clarify
T-INST-110's coverage scope around the SPEC 10.10 "sequentially in commit
order" requirement.

- T-TMP-11a: run() generator zero-iteration tmpdir non-creation
- T-TMP-08c: concurrent run() generator tmpdir isolation (closes the
  four-cell {independent / same project} x {generator / promise / CLI}
  matrix)
- T-API-21e2: run() generator counterpart to T-API-21e (absolute envFile)
- T-API-59d / T-API-59e: local envFile does not redirect global env-file
  lookup via XDG_CONFIG_HOME / HOME (extends T-API-59 series from
  RunOptions.env tier to local-env-file tier)
- T-TMP-11 / T-TMP-08a / T-API-21e: clarify each is runPromise-specific
- T-INST-110: clarify that the test pins sequentiality but not the
  auto-install-order-equals-file-commit-order relation, which is not
  externally observable in SPEC

Appendix A traceability rows updated for SPEC 7.4, 8.1, 8.2, 8.3, 9.1,
9.2, 9.5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes five surface-parity / cleanup-trigger gaps surfaced in this
review cycle:

- T-TMP-16g/h/i: cleanup on bare-name goto resolving to missing script
  in the current workflow, across CLI / runPromise / run surfaces
  (complements T-TMP-16a–f's qualified-target form).
- T-TMP-16j: cleanup on goto whose target violates the SPEC 4.1
  name-restriction pattern (complements T-TMP-16b's delimiter-syntax
  branch on the syntactic-invalidity axis).
- T-INST-116j3: SIGTERM-axis parity for T-INST-116j2 on the
  post-exit-first × non-zero-npm-exit composed window.
- T-API-56i: representative run()-surface coverage of the SPEC 9.5
  RunOptions.env filtering and structural-shape-acceptance matrix.
- T-API-64a2: run()-surface own-property duck-signal acceptance,
  closing the {own / prototype-inherited} × {run / runPromise} matrix.

Coverage matrix rows for SPEC 4.1, 7.2, 7.3, 7.4, 9.1, 9.2, 9.3, 9.5,
10.10, and 12 updated to reference the new tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply feedback identifying gaps in TEST-SPEC.md against SPEC.md:
- Extend T-API-65p / T-API-65q abort-precedence variants to cover
  throwing `env` proxy `get` trap on an included key (variant xv)
  and throwing `options.maxIterations` getter (variant xvi).
- Extend T-API-55c / T-API-55d zero-iteration snapshot-throw matrix
  with throwing `env` proxy `get` trap on an included key (variant d).
- Add `LOOPX_TEST_AUTOINSTALL_PAUSE=post-spawn-failure-first` ordinal
  window value plus the corresponding name-targeted form, and add
  T-INST-116m / T-INST-116m2 (SIGINT / SIGTERM) for the no-active-child
  signal contract on the npm-install-spawn-failure accumulator state.
- Add T-TMP-38f for SIGTERM-first racing-signal cleanup-idempotence
  parity (inverted observation order of T-TMP-38).
- Add T-SYM-02d for CLI symlinked-project-root effective-cwd
  device/inode identity, completing the {string-spelling, identity} ×
  {CLI, programmatic} matrix.
- Clarify T-TMP-38e tmpdir-path observation: harness scans the
  test-isolated TMPDIR parent for the single loopx-* entry left
  behind by `LOOPX_TEST_CLEANUP_FAULT=recursive-remove-fail`.
- Update Appendix A traceability matrix entries for sections 6.1,
  7.2, 7.3, and 10.10 to reference the new tests.

No SPEC.md or SPEC-PROBLEMS.md changes — feedback identified no
ADR-0004 SPEC ambiguities blocking clean test coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply post-acceptance feedback covering five areas: tighten T-INST-116i
/ 116i2 with a pre-signal lstat+content snapshot of the current
workflow's .gitignore so the SPEC 10.10 "side effects completed before
the signal observation remain on disk" rule is pinned without
over-specifying the safeguard-write timing window; add T-TMP-42c
(cleanup-fault × CLI signal × all three LOOPX_TEST_CLEANUP_FAULT seam
values × both signals) closing the remaining diagonal in the
cleanup-warning-does-not-affect-outcome matrix; pin the SPEC 10.10 "no
aggregate report when no failures" clause via explicit negative
assertions on T-INST-110 and T-INST-110c; introduce a suite-wide
env-absence robustness rule in section 4.7; and add T-API-51a2
(generator-surface counterpart for all five protocol variables) and a
PWD passthrough assertion in T-INST-115 for matrix symmetry. Open
SPEC-PROBLEMS.md with P-0004-01 to track auto-install commit-order
observability for a follow-up SPEC clarification cycle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolve P-0004-01 (auto-install commit-order observability) by amending
SPEC §10.10's "Trigger" bullet to make cross-workflow auto-install order
implementation-defined and not an external conformance contract. Delete
SPEC-PROBLEMS.md (P-0004-01 was the only entry). Update TEST-SPEC.md to
drop "commit order" framing in T-INST-110, withFakeNpm comments, the
LOOPX_TEST_AUTOINSTALL_PAUSE seam description, T-INST-114, T-INST-114b,
T-INST-114d, T-INST-112c, T-INST-112h, T-INST-116b/b2/h/i/j/j2/j3/k/l/m,
Section 9, and Appendix A in favor of "implementation-defined
auto-install order."

Add T-TMP-27a covering SPEC 9.2's "LOOPX_TMPDIR is created
asynchronously after `runPromise()` returns" clause via a synchronous
readdir snapshot taken on the same microtask tick as `runPromise()`'s
return.

Add T-API-59f and T-API-59g (CLI `-e` parity for the SPEC 8.1
"inherited environment" rule across XDG_CONFIG_HOME and HOME),
extending the local-env-file-does-not-redirect-global-env-file-lookup
coverage from the runPromise() surface (T-API-59d/e) to the CLI surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant